#include "childFunction.h"

///////////////////////////////////////////////////////////////////////////////////////////////////

std::string generateString(int length) {
	std::string s;
	for(int i=0; i<length; ++i) {
		char ch[2];
		ch[1]=0;
		ch[0]=(rand()%(122-97))+97;	//a do z
		s.append(ch);
	}
	return s;
}

bool putIntegerInCharArray(int number, char* destination, int* currPosition, int maxPosition) {
	std::string s1;
	std::stringstream stringStream;
	stringStream << number;
	s1 = stringStream.str();

	memcpy(destination+(*currPosition), s1.c_str(), s1.length());

	(*currPosition)+=s1.length();

	if((*currPosition)>maxPosition)
		return false;
	return true;
}

bool putStringInCharArray(std::string stringToPut, char* destination, int* currPosition, int maxPosition) {
	memcpy(destination+(*currPosition), stringToPut.c_str(), stringToPut.length());

	(*currPosition)+=stringToPut.length();

	if((*currPosition)>maxPosition)
		return false;
	return true;
}

template <class ValueType>
ValueType drawWithDistribution (std::vector<int> probabilityDistribution, std::vector<ValueType> values ) {
	int sum=0;
	//obliczamy sume wag
	for(unsigned int i=0; i<probabilityDistribution.size(); ++i)
		sum+=probabilityDistribution[i];

	//losujemy liczbe z zakres od 1 do sumy wag
	int drawnNumber=(rand()%(sum))+1;

	for(unsigned int i=0; i<probabilityDistribution.size(); ++i) {
		drawnNumber-=probabilityDistribution[i];
		if(drawnNumber <= 0 )
			return values[i];
	}
}

std::string createRequestContentsFromTuple(Tuple tuple, bool ifTrue) {
	std::string requestContents;
	std::stringstream stringStream;

	int sizeOfTuple=tuple.size();
	for(int i=0; i<sizeOfTuple; ++i) {
		if(tuple[i].isInteger) {
			stringStream<<"integer:";
			int opt=rand()%3;
			if(ifTrue) {
				switch(opt) {
					case 0:
					{
						stringStream<<tuple[i].eInteger;
						break;
					}
					case 1:
					{
							int diff=(maxIntegerValue-tuple[i].eInteger);
							if(diff) {
								int a=rand()%diff+tuple[i].eInteger;
								stringStream<<"<="<<a;
							}
							else
								stringStream<<tuple[i].eInteger;

							break;
					}
					case 2:
					{
						if(tuple[i].eInteger) {
							int a=rand()%tuple[i].eInteger;
							stringStream<<">="<<a;
						}
						else
							stringStream<<tuple[i].eInteger;
						break;
					}
				}
			}
			else {
				int a;
				do {
					a=rand()%maxIntegerValue;
				}while(a==tuple[i].eInteger);

				switch(opt) {
					case 0:
						stringStream<<a;
						break;
					case 1:
					case 2:
						if(a>tuple[i].eInteger)
							stringStream<<">="<<a;
						else
							stringStream<<"<="<<a;
						break;
				}
			}
		}
		else{	//>String
			stringStream<<"string:\"";
			if(ifTrue) {
				// @TODO rozbudować
				stringStream<<tuple[i].eString;
			}
			else {
				// @TODO rozbudować
				stringStream<<generateString(rand()%maxLengthString+1);
			}
			
			stringStream<<"\"";
		}
		stringStream<<",";
	}

	requestContents=stringStream.str();
	return (requestContents.substr(0, requestContents.length() - 1));
}

std::string createDrawnRequestContents () {
	std::string requestContents;
	std::stringstream stringStream;
	int sizeOfTuple=(rand()%(maxTupleSize-1))+1;
	for(int i=0; i<sizeOfTuple; ++i) {
		if(rand()%2) { //< integer
			stringStream<<"integer:";
			stringStream<<(rand()%maxIntegerValue);
			stringStream<<",";
		}
		else{	//>String
			stringStream<<"string:\"";
			stringStream<<generateString( ((rand()&(maxLengthString-1))+1) );
			stringStream<<"\",";
		}
	}
	requestContents=stringStream.str();
	return (requestContents.substr(0, requestContents.length() - 1));
}

void createOutputFileName(int numChild, char*fileOutputName) {
	for(int i=0; i<13; ++i) fileOutputName[i]=0;
	int position=0;
	putIntegerInCharArray(numChild, fileOutputName,&position,11);
	std::string s(".txt");
	putStringInCharArray(s, fileOutputName, &position, 14);
}
///////////////////////////////////////////////////////////////////////////////////////////////////

void createRequest(int numChild, char* requestBuffer, std::list<Tuple>& myPrivateTuples, int* lengthOfRequest /*out*/, int* sleepTime /*out*/, int* timeout /*out*/){
	srand(time(NULL));
    int position=0;

    //wrzucamy numChilda
    putIntegerInCharArray(numChild, requestBuffer, &position, SIZE_PIPE_DATA);

    //wrzucamy kropkę
    putStringInCharArray(std::string("."), requestBuffer, &position, SIZE_PIPE_DATA);

    //losujemy rodzaj operacji z zadanym rozkładem prawdopodobieństwa
    int drawnValue;
    {
	    std::vector<int> probabilityDistribution;
	    std::vector<int> values;
	    //
	    probabilityDistribution.push_back(2);
	    values.push_back(LOUTPUT);
		//
	    probabilityDistribution.push_back(1);
	    values.push_back(LINPUT);
	    //
	    probabilityDistribution.push_back(2);
	    values.push_back(LREAD);

	    drawnValue=drawWithDistribution(probabilityDistribution, values);
	}

    //wrzucamy <numer operacji>.(
	{
		char numOp[2];
		numOp[0]=drawnValue;
		numOp[1]=0;
		putStringInCharArray(std::string(numOp), requestBuffer, &position, SIZE_PIPE_DATA);
		putStringInCharArray(std::string("."), requestBuffer, &position, SIZE_PIPE_DATA);
	}


	if(drawnValue==LOUTPUT) {
		Tuple newTuple;// $$GEN_LOSOWA

		int numTupleElements = (rand() % (maxTupleSize-1))+1;

		for(int i=0; i<numTupleElements; ++i) {
			if(rand()%2) {
				//tworzym int
				int newInteger=(rand()%(maxIntegerValue-1)+1);
				putIntegerInCharArray(newInteger, requestBuffer, &position, SIZE_PIPE_DATA);
				putStringInCharArray(std::string(","), requestBuffer, &position, SIZE_PIPE_DATA);

				newTuple.push_back(Element(newInteger));
			}
			else{
				//tworzym string
				std::string st=generateString(rand()%maxLengthString+1);
				putStringInCharArray(std::string("\""), requestBuffer, &position, SIZE_PIPE_DATA);
				putStringInCharArray(st, requestBuffer, &position, SIZE_PIPE_DATA);
				putStringInCharArray(std::string("\","), requestBuffer, &position, SIZE_PIPE_DATA);

				newTuple.push_back(Element(st)); // $$GEN_LOSOWA
			}
		}
		--position;
		myPrivateTuples.push_back(newTuple);
	}
	else /* drawnValue==LINTPUT || drawnValue==LIREAD */{
		//losujemy czy pobieramy istniejącą krotkę czy nie;
		int ifChooseExisting;
		int chooseExisting=1, chooseNotExisting=0;
		//wybieramy rodzaj operacji i wymyślamy sensowne zapytania i dodania
		std::vector<int> probabilityDistribution;
		std::vector<int> values;
		//
		probabilityDistribution.push_back(3);
		values.push_back(chooseExisting);
		//
		probabilityDistribution.push_back(2);
		values.push_back(chooseNotExisting);
		//

		ifChooseExisting=drawWithDistribution(probabilityDistribution, values);

		std::list<Tuple>::iterator drawnTupleIterator;
		Tuple drawnTuple;
		//
		int drawnTupleIndex;
		if(!myPrivateTuples.size()) {
			drawnTupleIndex=0;
		}
		else
			drawnTupleIndex=rand()%myPrivateTuples.size();
		int i=0;
		for(std::list<Tuple>::iterator it=myPrivateTuples.begin(); ; ++it) {
			if(i==drawnTupleIndex) {
				drawnTuple=(*it);
				drawnTupleIterator=it;
				break;
			}
			++i;
		}
		//

		if(ifChooseExisting && !myPrivateTuples.empty()) {
			std::string s=createRequestContentsFromTuple(drawnTuple, true);
			putStringInCharArray(s, requestBuffer, &position, SIZE_PIPE_DATA);
		}
		else {	//żądanie nie wybierające krotki cośmy już ją dodali;
			int changeTuple=1, drawAsYouWish=0;
			int choose;

			//wybieramy rodzaj operacji i wymyślamy sensowne zapytania i dodania
			std::vector<int> probabilityDistribution;
			std::vector<int> values;
			//
			probabilityDistribution.push_back(3);
			values.push_back(changeTuple);
			//
			probabilityDistribution.push_back(0);
			values.push_back(drawAsYouWish);
			//

			choose=drawWithDistribution(probabilityDistribution, values);

			if(choose==drawAsYouWish || myPrivateTuples.empty()) {
				std::string s=createDrawnRequestContents();
				putStringInCharArray(s, requestBuffer, &position, SIZE_PIPE_DATA);
			}
			else {
				std::string s=createRequestContentsFromTuple(drawnTuple, false);
				putStringInCharArray(s, requestBuffer, &position, SIZE_PIPE_DATA);
			}

		}

		*lengthOfRequest=position+1;

		if(!myPrivateTuples.empty() && drawnValue==LINPUT)
			myPrivateTuples.erase(drawnTupleIterator);
		if(drawnValue==LREAD)
			;//nothing

	}

	//wrzucamy
	putStringInCharArray(std::string(" "), requestBuffer, &position, SIZE_PIPE_DATA);
	//wrzucamy kończące 0
	requestBuffer[--position]=0;

	//sleepTime
	(*sleepTime)=rand()%3+1;
	(*timeout)=rand()%3+1;
}

bool checkIfWithTimeout(char* buf);

bool createRequestFromFile(int numChild, char* requestBuffer, std::ifstream& sourceRequestFile, int* lengthOfRequest /*out*/, int* sleepTime /*out*/, int* timeout /*out*/) {
	while (sourceRequestFile.good()) {
		std::string line;
		getline (sourceRequestFile,line);
		if(line.length()==0 || line.length()==1) continue;

		//sleepTime
		std::string sleepTimeString= line.substr( line.find("S")+1);
		sleepTimeString=sleepTimeString.substr(0, line.find("|"));
		*sleepTime=atoi(sleepTimeString.c_str());

		//timeout
		if(checkIfWithTimeout(requestBuffer)) {
			std::string timeoutString= line.substr( line.find("T")+1);
			timeoutString=timeoutString.substr(0, line.find("|"));
			*timeout=atoi(timeoutString.c_str());

		}

		line=line.substr(line.find(".")-1);
		std::string numChildFromFileString = line.substr( 0, line.find(".") );
		if(numChildFromFileString.length()==0)
			break;
		int numChildFromFile=atoi(numChildFromFileString.c_str());
		if(numChild==numChildFromFile) {
			int pos=0;
			putStringInCharArray(line, requestBuffer, &pos, SIZE_PIPE_DATA);
			requestBuffer[pos]=0;
			*lengthOfRequest=pos+1;
			return false;
		}
	}
	return true;
}

void createCancelRequest(int numChild, char* requestBuffer, int* lengthOfRequest) {
	int position=0;

	//wrzucamy numChilda
	putIntegerInCharArray(numChild, requestBuffer, &position, SIZE_PIPE_DATA);

	//wrzucamy kropkę
	putStringInCharArray(std::string("."), requestBuffer, &position, SIZE_PIPE_DATA);

	//wrzucamy D==DELETE
	putStringInCharArray(std::string("D"), requestBuffer, &position, SIZE_PIPE_DATA);

	//wrzucamy konczace 0
	putStringInCharArray(std::string(" "), requestBuffer, &position, SIZE_PIPE_DATA);
	//wrzucamy kończące 0
	requestBuffer[--position]=0;

	*lengthOfRequest=position+1;
	return;
}

bool checkIfWithTimeout(char* buf) {
	std::string readBufferString(buf);
	std::string operation = readBufferString.substr(readBufferString.find(".")+1, 1);
	if(operation[0]=='O')
		return false;
	return true;
}

#define LINKED_TO_MAIN
//
//
void childFunction(int numChild, int* requestPipe,int* answerPipe, int startupCode, std::ifstream* sourceRequestFile)
{
	if(numChild==0) {
		while (1);
	}
	srand(time(NULL)+numChild*800);
	//stworzenie pliku zapisującego operacje potomka <nr_potomka>.txt
	std::ofstream myOutputFile;
	char myOutputFileName[15];
	createOutputFileName(numChild, myOutputFileName);
	myOutputFile.open(myOutputFileName);

    char readBuffer[SIZE_PIPE_DATA];
    char requestBuffer[SIZE_PIPE_DATA];
    std::list<Tuple> myPrivateTuples; // $$GEN_LOSOWA tutaj przechowujemy co wrzucił potomek do macierzystego

	semop(consoleSemaphore, &sembufWait, 1); //Wait
    std::cout<<"\n\n------->>POTOMEK"<<numChild<<":";
    std::cout<<"\n\tpotomek informuje, ze powstal";
    std::cout<<"\n------->>POTOMEK"<<numChild<<"."<<std::endl;
    semop(consoleSemaphore, &sembufPost, 1); //Post
    //
    myOutputFile<<"\n\n------->>POTOMEK"<<numChild<<":";
    myOutputFile<<"\n\tpotomek informuje, ze powstal";
	myOutputFile<<"\n------->>POTOMEK"<<numChild<<"."<<std::endl<<std::endl;

#ifdef LINKED_TO_MAIN
	close(requestPipe[READ]);
    close(answerPipe[WRITE]);
#endif

	for(int i=0; ; ++i)
	{
		int timeoutValue;
		int lengthOfRequest;
		int sleepTime;
		if(startupCode==startupCode_Automatic) {
			createRequest(numChild, requestBuffer, myPrivateTuples, &lengthOfRequest, &sleepTime, &timeoutValue);
		}
		else if(startupCode==startupCode_FromFile) {
			bool flag=createRequestFromFile(numChild, requestBuffer, *sourceRequestFile, &lengthOfRequest, &sleepTime, &timeoutValue);
			if( !sourceRequestFile->is_open() || flag )
				break;
		}


		if(sleepTime) {
			sleep(sleepTime);
		}
#ifdef LINKED_TO_MAIN
		write(requestPipe[WRITE], requestBuffer, strlen(requestBuffer)); //WRITE-------------------------------------

		bool ifWithTimeout=checkIfWithTimeout(requestBuffer);

#endif
		//
		semop(consoleSemaphore, &sembufWait, 1); //Wait
		std::cout<<"\n\n------->>POTOMEK"<<numChild<<":";
		std::cout<<"\n\tPotomek wyslal requesta o tresci:\n\t\t"<<requestBuffer;
		if(!ifWithTimeout) {
			std::cout<<"\n\t\tSLEEPTIME="<<sleepTime;
		}
		else
			std::cout<<"\n\t\tTIMEOUT="<<timeoutValue<<" SLEEPTIME="<<sleepTime;
		std::cout<<"\n------->>POTOMEK"<<numChild<<"."<<std::endl<<std::endl;
		semop(consoleSemaphore, &sembufPost, 1); //Post
		//
		myOutputFile<<"\n\n------->>POTOMEK"<<numChild<<":";
		myOutputFile<<"\n\tPotomek wyslal requesta o tresci:\n\t\t"<<requestBuffer;
		if(!ifWithTimeout) {
			myOutputFile<<"\n\t\tSLEEPTIME="<<sleepTime;
		}
		else
			myOutputFile<<"\n\t\tTIMEOUT="<<timeoutValue<<" SLEEPTIME="<<sleepTime;
		myOutputFile<<"\n------->>POTOMEK"<<numChild<<"."<<std::endl<<std::endl;



#ifdef LINKED_TO_MAIN
		//
		fd_set fds;	//grupa deskryptorow
		FD_ZERO(&fds); // zerujemy je
		struct timeval tv = { 0 };
		tv.tv_sec=timeoutValue;
		FD_SET(answerPipe[READ], &fds); //dodajemy deskryptor pajpa do grupy
		int wynikSelect = select(answerPipe[READ]+1, &fds, NULL, NULL, &tv);
		if ( ifWithTimeout && (wynikSelect <= 0)) {
			createCancelRequest(numChild, requestBuffer, &lengthOfRequest);
			write(requestPipe[WRITE], requestBuffer, lengthOfRequest);

			//drukowanie
			semop(consoleSemaphore, &sembufWait, 1); //Wait
			std::cout<<"\n\n------->>POTOMEK"<<numChild<<":";
			std::cout<<"\n\tPotomek wyslal requesta o anulowanie ostatniego zadania z powodu przekroczenia timeouta.";
			std::cout<<"\n------->>POTOMEK"<<numChild<<"."<<std::endl<<std::endl;
			semop(consoleSemaphore, &sembufPost, 1); //Post
			//
			myOutputFile<<"\n\n------->>POTOMEK"<<numChild<<":";
			myOutputFile<<"\n\tPotomek wyslal requesta o anulowanie ostatniego zadania z powodu przekroczenia timeouta.";
			myOutputFile<<"\n------->>POTOMEK"<<numChild<<"."<<std::endl<<std::endl;
		}
		for(int j=0; j<SIZE_PIPE_DATA; ++j) readBuffer[j]=0;
		read(answerPipe[READ], readBuffer, SIZE_PIPE_DATA); //READ-----------------------------------------------
#endif
		//
		semop(consoleSemaphore, &sembufWait, 1); //Wait
		std::cout<<"\n\n------->>POTOMEK"<<numChild<<":";
		std::cout<<"\n\tPotomek odebral odpowiedz o tresci:\n\t\t"<<readBuffer;
		std::cout<<"\n------->>POTOMEK"<<numChild<<"."<<std::endl<<std::endl;
		//
		myOutputFile<<"\n\n------->>POTOMEK"<<numChild<<":";
		myOutputFile<<"\n\tPotomek odebral odpowiedz o tresci:\n\t\t"<<readBuffer;
		myOutputFile<<"\n------->>POTOMEK"<<numChild<<"."<<std::endl<<std::endl;
		semop(consoleSemaphore, &sembufPost, 1); //Post

		if(startupCode==startupCode_Automatic) {
			if(i==NUM_REQUESTS_CHILD)
				break;
		}
		for(int j=0; j<SIZE_PIPE_DATA; ++j) readBuffer[j]=0;
	}

    myOutputFile.close();

    while(1); //żeby nam nie poumarly dzieciotka

}
